package cn.sylinx.hbatis.ext.xmapper.repository;

import java.util.List;
import java.util.Map;

import cn.sylinx.hbatis.db.common.Page;
import cn.sylinx.hbatis.db.common.Record;
import cn.sylinx.hbatis.db.mapper.QueryMapper;
import cn.sylinx.hbatis.ext.common.repository.CommonDaoServiceImpl;
import cn.sylinx.hbatis.ext.common.spi.HbatisServiceManager;
import cn.sylinx.hbatis.ext.parse.SqlParser;
import cn.sylinx.hbatis.ext.res.StatementHandler;
import cn.sylinx.hbatis.ext.xmapper.XmapperCacheQuery;
import cn.sylinx.hbatis.ext.xmapper.spi.XmapperServiceManager;
import cn.sylinx.hbatis.ext.xmapper.xml.QueryMapping;
import cn.sylinx.hbatis.ext.xmapper.xml.Sql;
import cn.sylinx.hbatis.ext.xmapper.xml.SqlForUse;
import cn.sylinx.hbatis.ext.xmapper.xml.XmlSqlMapper;
import cn.sylinx.hbatis.kit.StrKit;
import cn.sylinx.hbatis.kit.Tuple;

public class DaoServiceImpl extends CommonDaoServiceImpl implements DaoService {

	public DaoServiceImpl() {

	}

	public DaoServiceImpl(Repository repository) {
		super(repository);
	}

	@Override
	public <T> List<T> query(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];
		return query(namespaceId, id, params);
	}

	@Override
	public Map<String, Object> queryFirstForMap(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryFirstMap(st, pms);
	}

	@Override
	public Map<String, Object> queryFirstForMap(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryFirstForMap(namespaceId, id, params);
	}

	@Override
	public List<Map<String, Object>> queryForMapList(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryForMapList(namespaceId, id, params);
	}

	@Override
	public List<Record> queryForRecords(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryRecords(st, pms);
	}

	@Override
	public List<Record> queryForRecords(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryForRecords(namespaceId, id, params);
	}

	@Override
	public Record queryFirstRecord(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryFirstRecord(st, pms);
	}

	@Override
	public Record queryFirstRecord(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryFirstRecord(namespaceId, id, params);
	}

	@Override
	public List<Object[]> queryObjectArrayList(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).query(st, pms);
	}

	@Override
	public List<Object[]> queryObjectArrayList(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryObjectArrayList(namespaceId, id, params);
	}

	/**
	 * 带有StatementHandler
	 * 
	 * @param statement
	 * @param params
	 * @return
	 */
	private Tuple getQueryStatement(String statement, Map<String, Object> params) {

		StatementHandler sqlHandler = null;
		if (params != null) {
			Object obj = params.get(StatementHandler.class.getName());
			if (obj != null && (obj instanceof StatementHandler)) {
				sqlHandler = (StatementHandler) obj;
			}
		}
		return SqlParser.parseSql(statement, params, sqlHandler);
	}

	@Override
	public Object[] queryFirstObjectArray(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryFirst(st, pms);
	}

	@Override
	public Object[] queryFirstObjectArray(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryFirstObjectArray(namespaceId, id, params);
	}

	@Override
	public List<Map<String, Object>> queryForMapList(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryMap(st, pms);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> query(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		final QueryMapping qm = sql.getQueryMapping();
		if (qm == null) {
			throw new IllegalArgumentException("参数错误");
		}

		QueryMapper<T> qmapper = new QueryMapper<T>() {

			@Override
			public Map<String, String> getJdbcToJavaMapper() {
				return qm.getMap();
			}

			@Override
			public Class<T> getValueObjectClass() {

				try {
					String clzStr = qm.getReturnClass();
					if (StrKit.isNotBlank(clzStr)) {
						return (Class<T>) Class.forName(clzStr);
					}
					return null;
				} catch (Exception e) {
					return null;
				}

			}
		};

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).query(st, qmapper, pms);
	}

	@Override
	public <T> Page<T> queryPage(String namespaceId, String id, int pageNumber, int pageSize,
			Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		final QueryMapping qm = sql.getQueryMapping();
		if (qm == null) {
			throw new IllegalArgumentException("参数错误");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String preSql = (String) tp.get(0);
		Object[] prePms = (Object[]) tp.get(1);

		Tuple t = getDialect().getSqlBuilder().buildPaginatorSql(preSql, pageNumber, pageSize);
		String sqlCount = t.getObject(0);
		Record r = queryRecordWithSql(sqlCount, prePms);
		int totalRow = r == null ? 0 : Integer.valueOf(r.get("totalCount").toString());
		if (totalRow == 0) {
			// 空
			Page<T> emptyPage = new Page<T>();
			emptyPage.setPageSize(pageSize);
			return emptyPage;
		}

		int totalPage = (int) (totalRow / pageSize);
		if (totalRow % pageSize != 0) {
			totalPage++;
		}

		String sqlLimit = t.getObject(1);
		Object[] pms = t.getObject(2);

		int paramSize = prePms == null ? 0 : prePms.length;
		int pageParamSize = pms == null ? 0 : pms.length;
		int finalParamSize = paramSize + pageParamSize;
		Object[] finalParams = new Object[finalParamSize];

		// 查询参数
		if (paramSize > 0) {
			for (int i = 0; i < paramSize; ++i) {
				finalParams[i] = prePms[i];
			}
		}

		// 分页参数
		if (pageParamSize > 0) {
			for (int i = 0; i < pageParamSize; ++i) {
				finalParams[i + paramSize] = pms[i];
			}
		}

		QueryMapper<T> qmapper = new QueryMapper<T>() {

			@Override
			public Map<String, String> getJdbcToJavaMapper() {
				return qm.getMap();
			}

			@SuppressWarnings("unchecked")
			@Override
			public Class<T> getValueObjectClass() {

				try {
					String clzStr = qm.getReturnClass();
					if (StrKit.isNotBlank(clzStr)) {
						return (Class<T>) Class.forName(clzStr);
					}
					return null;
				} catch (Exception e) {
					return null;
				}
			}
		};

		List<T> dataList = HbatisServiceManager.getHbatisService().use(getDatasourceName()).query(sqlLimit, qmapper,
				finalParams);
		Page<T> page = new Page<T>(dataList, pageNumber, pageSize, totalPage, totalRow);
		return page;
	}

	@Override
	public <T> Page<T> queryPage(String sqlId, int pageNumber, int pageSize, Map<String, Object> params) {
		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];
		return queryPage(namespaceId, id, pageNumber, pageSize, params);
	}

	@Override
	public Page<Record> queryPageRecords(String namespaceId, String id, int pageNumber, int pageSize,
			Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String preSql = (String) tp.get(0);
		Object[] prePms = (Object[]) tp.get(1);

		Tuple t = getDialect().getSqlBuilder().buildPaginatorSql(preSql, pageNumber, pageSize);
		String sqlCount = t.getObject(0);
		Record r = queryRecordWithSql(sqlCount, prePms);
		int totalRow = r == null ? 0 : Integer.valueOf(r.get("totalCount").toString());
		if (totalRow == 0) {
			// 空
			Page<Record> emptyPage = new Page<Record>();
			emptyPage.setPageSize(pageSize);
			return emptyPage;
		}

		int totalPage = (int) (totalRow / pageSize);
		if (totalRow % pageSize != 0) {
			totalPage++;
		}

		String sqlLimit = t.getObject(1);
		Object[] pms = t.getObject(2);

		int paramSize = prePms == null ? 0 : prePms.length;
		int pageParamSize = pms == null ? 0 : pms.length;
		int finalParamSize = paramSize + pageParamSize;
		Object[] finalParams = new Object[finalParamSize];

		// 查询参数
		if (paramSize > 0) {
			for (int i = 0; i < paramSize; ++i) {
				finalParams[i] = prePms[i];
			}
		}

		// 分页参数
		if (pageParamSize > 0) {
			for (int i = 0; i < pageParamSize; ++i) {
				finalParams[i + paramSize] = pms[i];
			}
		}

		List<Record> dataList = queryRecordsWithSql(sqlLimit, finalParams);
		Page<Record> page = new Page<Record>(dataList, pageNumber, pageSize, totalPage, totalRow);
		return page;
	}

	@Override
	public Page<Record> queryPageRecords(String sqlId, int pageNumber, int pageSize, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return queryPageRecords(namespaceId, id, pageNumber, pageSize, params);
	}

	@Override
	public <T> T queryFirst(String sqlId, Map<String, Object> params) {

		List<T> list = query(sqlId, params);

		return list != null && !list.isEmpty() ? list.get(0) : null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T queryFirst(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.QUERY.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not query");
		}

		final QueryMapping qm = sql.getQueryMapping();
		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String st = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		QueryMapper<T> qmapper = null;

		if (qm != null) {
			qmapper = new QueryMapper<T>() {

				@Override
				public Map<String, String> getJdbcToJavaMapper() {
					return qm.getMap();
				}

				@Override
				public Class<T> getValueObjectClass() {

					try {
						String clzStr = qm.getReturnClass();
						if (StrKit.isNotBlank(clzStr)) {
							return (Class<T>) Class.forName(clzStr);
						}
						return null;
					} catch (Exception e) {
						return null;
					}

				}
			};
		}

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).queryFirst(st, qmapper, pms);
	}

	@Override
	public int update(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return update(namespaceId, id, params);
	}

	@Override
	public int update(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.UPDATE.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not update for use");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String updateSql = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).update(updateSql, pms);
	}

	@Override
	public int delete(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return delete(namespaceId, id, params);
	}

	@Override
	public int delete(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.DELETE.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not delete for use");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String deleteSql = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).update(deleteSql, pms);
	}

	@Override
	public Object save(String sqlId, Map<String, Object> params) {

		String[] ids = sqlId.split("\\.");
		String namespaceId = ids[0];
		String id = ids[1];

		return save(namespaceId, id, params);
	}

	@Override
	public Object save(String namespaceId, String id, Map<String, Object> params) {

		if (!XmlSqlMapper.get().isInited()) {
			throw new RuntimeException("xmlSqlmapper plugin not started~");
		}

		Sql sql = XmlSqlMapper.get().getSqlBySqlId(namespaceId, id);

		String forUse = sql.getForUse();
		if (!SqlForUse.INSERT.getCode().equals(forUse)) {
			throw new IllegalArgumentException("sql is not insert for use");
		}

		Tuple tp = getQueryStatement(sql.getStatement(), params);
		String insertSql = (String) tp.get(0);
		Object[] pms = (Object[]) tp.get(1);

		return HbatisServiceManager.getHbatisService().use(getDatasourceName()).save(insertSql, pms);
	}

	@Override
	public XmapperCacheQuery withCache() {
		return XmapperServiceManager.getXmapperService().useService(getDatasourceName()).withXmapperCache();
	}

}
